Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HTTP: Warning and test fix #943

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open

HTTP: Warning and test fix #943

wants to merge 10 commits into from

Conversation

heifner
Copy link
Member

@heifner heifner commented Oct 16, 2024

  • Fixes compiler warning about this not used by lambda
  • Add additional wait for no bytes in flight.
    • On my machine, it takes awhile for the http_plugin to react to the close of the sockets and clear out the bytes in flight.
    • Not sure why this requires such a time on my machine. Maybe WSL2 connections are really slow.

@@ -606,6 +606,8 @@ BOOST_FIXTURE_TEST_CASE(bytes_in_flight, http_plugin_test_fixture) {
req.set(http::field::host, "127.0.0.1:8891");
boost::beast::http::write(s, req);
}
//make sure got to the point http threads had responses queued
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why does it matter if the response is queued or not at this point?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This avoids boost::beast::http::status::service_unavailable on my machine

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe after the boost::beast::http::write(s, req) try doing a s.wait(wait_read), that will guarantee that the response is in flight. just a guess though

@@ -606,6 +606,8 @@ BOOST_FIXTURE_TEST_CASE(bytes_in_flight, http_plugin_test_fixture) {
req.set(http::field::host, "127.0.0.1:8891");
boost::beast::http::write(s, req);
}
//make sure got to the point http threads had responses queued
std::this_thread::sleep_for(std::chrono::seconds(5));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why here sleeping 5 seconds but below sleeping 1 second?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the 1s is not for 4mb messages.

//now rip these connections out before the responses are completely sent
connections.clear();
//send some requests that should work still
send_4mb_requests(8u);
r = drain_http_replies();
for (const auto& e : r) {
ilog( "response: ${r}, count: ${c}", ("r", std::string(obsolete_reason(e.first)))("c", e.second));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I almost thought the r in ${r} was the r in r = drain_http_replies().

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed

Copy link
Member

@spoonincode spoonincode left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should be adding sleeps to tests

@heifner heifner added the OCI Work exclusive to OCI team label Oct 16, 2024
@@ -642,13 +644,14 @@ BOOST_FIXTURE_TEST_CASE(bytes_in_flight, http_plugin_test_fixture) {

//load up some more requests that exceed max
send_4mb_requests(32u);
//make sure got to the point http threads had responses queued
std::this_thread::sleep_for(std::chrono::seconds(1));
//now rip these connections out before the responses are completely sent
connections.clear();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understood your description right, this is the area that is problematic. Between this line and the line below the http plugin needs to have cleaned up all its ownstanding async_writes. Just closing the socket here does indeed send the FIN but the http threads need time to wake up and handle that error on the async_write to then clear out what's in flight.

I wonder if what might work is to shutdown each connection and then async_read on each one until getting confirmation the remote side disconnected via an error code.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would likely work. I added a check for bytes_in_flight since that is really what we want to test anyway. We want to verify that bytes in flight goes to 0 once connections are closed.

@ericpassmore
Copy link
Contributor

Note:start
category: System Stability
component: HTTP
summary: Fix warnings and test.
Note:end


//8 requests to start with
send_requests(8u);
std::unordered_map<boost::beast::http::status, size_t> r = scan_http_replies();
BOOST_REQUIRE_EQUAL(r[boost::beast::http::status::ok], 8u);
connections.clear();
wait_for_no_bytes_in_flight();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this test case has to do with requests in flight, not bytes, so this feels mismatched but maybe it still serves enough of its purpose?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A requests in flight would be better and useful to verify in both cases. I'll add it.

@@ -604,6 +604,7 @@ BOOST_FIXTURE_TEST_CASE(bytes_in_flight, http_plugin_test_fixture) {
boost::beast::http::request<boost::beast::http::empty_body> req(boost::beast::http::verb::get, "/4megabyte", 11);
req.keep_alive(true);
req.set(http::field::host, "127.0.0.1:8891");
s.wait(boost::asio::ip::tcp::socket::wait_write);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the purpose of this and the wait_read below? Why would the socket not be writable here? And if it isn't writable why does it matter before a sync write is issued?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not needed. I'll remove.

@@ -622,6 +622,12 @@ BOOST_FIXTURE_TEST_CASE(bytes_in_flight, http_plugin_test_fixture) {
return count_of_status_replies;
};

auto wait_for_no_bytes_in_flight = [&](uint16_t max = std::numeric_limits<uint16_t>::max()) {
while (http_plugin->bytes_in_flight() > 0 && http_plugin->requests_in_flight() > 0 && --max)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When waiting for no bytes in flight why do we need to look at requests in flight too?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No "need", just thought it was good to also verify requests_in_flight appropriately hits 0.

@@ -646,9 +652,13 @@ BOOST_FIXTURE_TEST_CASE(bytes_in_flight, http_plugin_test_fixture) {
std::this_thread::sleep_for(std::chrono::seconds(1));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we get rid of this final sleep? It seems like waiting for each connection to indicate it's readable would suffice. But if we need to make another busy loop maybe we could wait to see requests_in_flight is 32

@@ -623,7 +623,7 @@ BOOST_FIXTURE_TEST_CASE(bytes_in_flight, http_plugin_test_fixture) {
};

auto wait_for_no_bytes_in_flight = [&](uint16_t max = std::numeric_limits<uint16_t>::max()) {
while (http_plugin->bytes_in_flight() > 0 && http_plugin->requests_in_flight() > 0 && --max)
Copy link
Contributor

@greg7mdp greg7mdp Oct 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like this shouldn't make a difference as they should either both be 0 or both non-zero.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused by that too. And I don't understand the discrepancy why wait_for_no_bytes_in_flight looks at both while wait_for_no_requests_in_flight looks at only one. If the intent is to look at both then a common wait_for_nothing_in_flight could be made that looks at both.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In practice the requests in flight also matter. The issue is that bytes in flight can reach 0 before all the requests are done (or started).

bytes_in_flight is only incremented on sending the response. We do not, currently at least, include the request.

virtual void send_response(std::string&& json, unsigned int code) final {
auto payload_size = json.size();
increment_bytes_in_flight(payload_size);

Therefore, requests can be in progress, but bytes_in_flight can be 0. So we need to wait until all requests are complete to know that all bytes_in_flight are accounted for.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So could we just wait for requests_in_flight() to be 0, and then assert that bytes_in_flight() is 0 as well?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, that sounds better

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
OCI Work exclusive to OCI team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants